package com.sq.es.templete;


import com.sq.common.ana.annotation.DocField;
import com.sq.es.config.ElasticSearchConfig;
import com.sq.es.entry.EsRange;
import com.sq.common.enums.OrderType;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.HttpAsyncResponseConsumerFactory;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Objects;

/**
 * @Author Li sir
 * @Date 2024/1/23
 *
 * es复合查询
 *
 */

@Component
public class ElasticSearchCompositeTemplete {


    @Autowired
    private ElasticSearchConnectionTemplete elasticSearchConnectionTemplete;

    @Autowired
    private ElasticSearchConfig elasticSearchConfig;

    private static final RequestOptions COMMON_OPTION;

    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        builder.setHttpAsyncResponseConsumerFactory
                (new HttpAsyncResponseConsumerFactory.HeapBufferedResponseConsumerFactory(500 * 1024 * 1024));
        COMMON_OPTION = builder.build();
    }

    /**
     * 获取库中全部条数
     * @param indexName
     * @return
     */
    public Long getCount(String indexName){
        RestHighLevelClient client = elasticSearchConnectionTemplete.getConnection();
        // 构造查询请求
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
                .trackTotalHitsUpTo(elasticSearchConfig.getTrackTotalHitsUp())
                .size(0)
                .timeout(TimeValue.MAX_VALUE);

        sourceBuilder.query(QueryBuilders.matchAllQuery()); // 此处可以修改为自己的查询条件
        searchRequest.source(sourceBuilder);
        try {
            // 发送查询请求并获取响应
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            long totalHits = response.getHits().getTotalHits().value;
            System.out.println("Total hits: " + totalHits);
            return totalHits;
        } catch (Exception e) {
            throw new RuntimeException();
        }finally {
            elasticSearchConnectionTemplete.replaceClient(client);
        }
    }


    /**
     * 按照条件查询数量
     * @param indexName 索引名称
     * @param t 实体类型
     * @param esRange 范围查询
     * @return 数量
     * @param <T>
     */
    public <T > Long getCount(String indexName, T t, EsRange esRange){
        RestHighLevelClient connection = elasticSearchConnectionTemplete.getConnection();
        try {
            // 构造查询请求
            SearchRequest searchRequest = new SearchRequest(indexName);
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
                    .trackTotalHitsUpTo(elasticSearchConfig.getTrackTotalHitsUp())
                    .size(0)
                    .timeout(TimeValue.MAX_VALUE);
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            Class<?> aClass = t.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields){
                DocField docField = field.getAnnotation(DocField.class);
                if (Objects.isNull(docField)){
                    continue;
                }
                field.setAccessible(true);
                Object o = field.get(t);
                if (Objects.isNull(o)){
                    continue;
                }

                // 添加范围查询
                if (docField.rangeAnalyzer() && !Objects.isNull(esRange)){
                    Object startRange = esRange.getStartRange();
                    Object endRange = esRange.getEndRange();
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(field.getName()).from(startRange).to(endRange);
                    boolQueryBuilder.must(rangeQueryBuilder);
                    continue;
                }
                // 普通查询
                if (docField.queryAnalyzer()){
                    boolQueryBuilder.must(QueryBuilders.matchQuery(field.getName(),o));
                }
            }
            sourceBuilder.query(boolQueryBuilder);
            searchRequest.source(sourceBuilder);

            //执行查询，得到查询响应对象
            SearchResponse response = connection.search(searchRequest, COMMON_OPTION);
            long totalHits = response.getHits().getTotalHits().value;
            System.out.println("Total hits: " + totalHits);
            return totalHits;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            elasticSearchConnectionTemplete.replaceClient(connection);
        }
    }

    /**
     * 查询特定字段
     * @param indexName 库名称
     * @param t 数据
     * @param searchFiled 查询的字段
     * @return
     * @param <T>
     */
    public <T> SearchResponse search(String indexName,T t,String[] searchFiled,EsRange esRange){

        // 1. 获取es 连接
        RestHighLevelClient connection = elasticSearchConnectionTemplete.getConnection();

        // 2. 指定查询哪个索引库
        SearchRequest searchRequest = new SearchRequest(indexName);

        // 3. 创建搜索对象
        try {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                    .trackTotalHitsUpTo(elasticSearchConfig.getTrackTotalHitsUp())
                    .size(0)
                    .timeout(TimeValue.MAX_VALUE);
            Class<?> aClass = t.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields){
                DocField docField = field.getAnnotation(DocField.class);
                if (Objects.isNull(docField)){
                    continue;
                }
                field.setAccessible(true);
                Object o = field.get(t);
                if (Objects.isNull(o)){
                    continue;
                }
                if (docField.rangeAnalyzer() && !Objects.isNull(esRange)){
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(field.getName()).gte(esRange.getStartRange()).lte(esRange.getEndRange());
                    boolQueryBuilder.must(rangeQueryBuilder);
                    continue;
                }
                if (docField.queryAnalyzer()){
                    boolQueryBuilder.must(QueryBuilders.matchPhraseQuery(field.getName(),o));
                }

            }
            SearchSourceBuilder query = searchSourceBuilder.query(boolQueryBuilder);
            query.fetchSource(searchFiled,null);
            query.size(elasticSearchConfig.getMaxSearchNum());
            SearchRequest source = searchRequest.source(query);
            SearchResponse search = connection.search(source, RequestOptions.DEFAULT);
            return search;
        }catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            elasticSearchConnectionTemplete.replaceClient(connection);
        }
    }


    public <T> SearchResponse searchByPage(String indexName,T t,Integer pageNum,Integer pageSize,String[] includeFile,EsRange esRange){

        // 1. 获取es 连接
        RestHighLevelClient connection = elasticSearchConnectionTemplete.getConnection();

        // 2. 指定查询哪个索引库
        SearchRequest searchRequest = new SearchRequest(indexName);

        // 3. 创建搜索对象
        try {
//            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                    .trackTotalHitsUpTo(1000000000)
                    .timeout(TimeValue.timeValueSeconds(30)); // 超时时间设置
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            Class<?> aClass = t.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields){
                DocField docField = field.getAnnotation(DocField.class);
                if (Objects.isNull(docField)){
                    continue;
                }
                field.setAccessible(true);
                Object o = field.get(t);
                if (Objects.isNull(o)){
                    continue;
                }
                if (docField.rangeAnalyzer()){
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(field.getName()).gte(esRange.getStartRange()).lte(esRange.getEndRange());
                    boolQueryBuilder.must(rangeQueryBuilder);
                    continue;
                }
                if (docField.queryAnalyzer()){
                    boolQueryBuilder.must(QueryBuilders.matchPhraseQuery(field.getName(),o));
                }

                if (!docField.orderBy().equals(OrderType.NO)){
                    String s = docField.orderBy().name();
                    SortOrder sortOrder = null;
                    if ("asc".equals(s)){
                        sortOrder = SortOrder.ASC;
                    }else {
                        sortOrder = SortOrder.DESC;
                    }
                    searchSourceBuilder.sort(SortBuilders.fieldSort(field.getName()).order(sortOrder));
                }

            }
            SearchSourceBuilder query = searchSourceBuilder.query(boolQueryBuilder);
            int from = 0;

            if(pageNum > 1){
                from = ((pageNum - 1) *  pageSize) - 1;
            }
            query.from(from);
            query.size(pageSize);
            query.fetchSource(includeFile,null);
            SearchRequest source = searchRequest.source(query);
            SearchResponse search = connection.search(source, RequestOptions.DEFAULT);
            return search;
        }catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            elasticSearchConnectionTemplete.replaceClient(connection);
        }
    }


    public <T>  SearchResponse searchByMoreNoPage(String indexName,T t,EsRange esRange){
        Long count = getCount(indexName, t,esRange);
        System.out.println("共:" + count + "条数据");
        RestHighLevelClient connection = elasticSearchConnectionTemplete.getConnection();
        try {
            //构建查询请求
            SearchRequest searchRequest = new SearchRequest(indexName);
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            Class<?> aClass = t.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields){
                DocField docField = field.getAnnotation(DocField.class);
                if (Objects.isNull(docField)){
                    continue;
                }
                field.setAccessible(true);
                Object o = field.get(t);
                if (Objects.isNull(o)){
                    continue;
                }
                if (docField.queryAnalyzer()){
                    boolQueryBuilder.must(QueryBuilders.matchPhraseQuery(field.getName(),o));
                }
                boolQueryBuilder.must(QueryBuilders.matchQuery(field.getName(),o));


            }
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(boolQueryBuilder);

            //设置当前查询请求的筛选
            searchRequest.source(searchSourceBuilder);
            searchSourceBuilder.from(0);
            searchSourceBuilder.size(Integer.parseInt(count.toString()));
            //执行查询，得到查询响应对象
            SearchResponse response = connection.search(searchRequest, RequestOptions.DEFAULT);

            return response;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            elasticSearchConnectionTemplete.replaceClient(connection);
        }

    }

}
